<?php
// resource manager
// dynamic resource loading
// multiple apps loaded at once client side
// tiled app view
// scheduled wapp -> wapi ping
// every 15 seconds or so. load balancing server side sets schedule client side
//

// nohup sh -c "sleep 15;nohup wget -O /dev/null http://192.168.0.130/TESTING.PHP > /dev/null 2>&1" > /dev/null 2>&1 & echo $!
//
//		$backtrace = debug_backtrace();
//		if (isset($backtrace[1])) {
//			$cfile = $backtrace[1]['file'];
//			$cclass = $backtrace[1]['class'];
//			$cfunc = $backtrace[1]['function'];
//		} else {
//			$stop = '';
//			$app_class = $this->mem['current_app_class'];
//		}

if (session_id() == '') session_start();

header('Content-Type: text/html; charset=utf-8');

$docroot = rtrim(getenv('DOCUMENT_ROOT'),'/');
// Timezone Fix
require_once($docroot.'/lib/wapi/tz_fix.php');
require_once($docroot.'/lib/wapi/wapi_ext.php');
require_once($docroot.'/lib/wapi/app_base.php');
require_once($docroot.'/lib/wapi/wapi_mod_base.php');
require_once($docroot.'/lib/phpQuery/phpQuery.php');

//class wapi_response_data {
//	var $app; 				// the app sending this response
//	var $class;				// the class sending this response
//	var $func;				// the function sending this response
//	var $text = '';			// any text echoed by this app
//	var $cmds = Array();	// javascript commands this app is requesting ran
//	var $errors = Array();	// error array for this app
//	var $data = null;		// function called return value (whether polled, com
//}
//class wapi_response {
//	var $com = null;		// app_response_data of app com
//	var $poll = array(); 	// app_response_data of polled running apps and wapi
//}
class wapi_response {
	public $app = '';
	public $text = '';			// any text echoed by this app
	public $cmds = Array();		// javascript commands this app is requesting ran
	public $errors = Array();	// error array for this app
	public $data = null;		// function called return value (whether polled, com
}
class wapi_complete_response {
	public $poll_resp = array();
	public $req_resp = null;
}
class wapi_user {
	public $user_id = '';
	public $info = array();
	public $app_list = array();
	public $running = array();
}
class wapi_job {
	public $type = ''; 	// type can be com = processing a com request, poll = running app polls
	public $app = '';	// the app this job is running on
}

class wapi {
	public $base_path = '';
	public $apps_path = '/apps'; // app folder
	public $lib_path = '/lib';   // addon library folder
	public $wapi_path = '/lib/wapi';   // addon library folder
	public $db_base_path = '';
	public $db;
	public $os = 'Unknown';
	public $os_version = '??.??';

	public $job = null;

	public $device_tag;

	public $user;

	public $req = null; 	// Request data
	public $resp = null; 	// Response data

	public $mods = array();
	public $com_prefix = 'com_';
	public $event_prefix = 'event_';

	public $has_focus = '';

	public $firstrun = false;
	public $firstrun_user = false;
	public $firstrun_session = false;
	// User specific data
	public $app_has_focus = 'wapi';
	public $db_user;

	private $db_lib = 'db_sqlite_lib';
	private $session_vars = array();
	private $xsession_vars = array();
	private $ran = false;
	private $class = 'wapi';

	function __construct(){
		$this->job = new wapi_job();
		$this->user = new wapi_user();
		$this->resp = new wapi_complete_response();
		$this->resp->req_resp = new wapi_response();
		if (isset($_COOKIE['WAPP_device_tag'])) {
			$this->device_tag = $_COOKIE['WAPP_device_tag'];
		}
		if (isset($_SESSION['wapp_user_id'])){
			$this->user->user_id = $_SESSION['wapp_user_id'];
		}
		$this->session_vars[] = 'session_id';
		$this->xsession_vars[] = 'device_id';
	}
	function __destruct(){
		//$_SESSION['wapp_user_id'] = $this->user->user_id;
	}

	private function init_secure(){
		// needs to be done whenever user_id changes
		$this->secure = null;
		$this->secure = new secure();
		$this->secure->salt1 = 'pass';
		$this->secure->salt2 = '';
		$this->secure->salt3 = '';
		$this->secure->enabled = true;

		$test = $this->secure->enc_key;

		$tmp = '';
	}
	function _autoload($class_name){
		if (strpos($class_name,'_lib') > 0 && strlen($class_name) - strpos($class_name,'_lib') === 4){
			$this->load_lib_file($class_name);
		} else if (strpos($class_name,'fs_') === 0 || $class_name === 'fs'){
			$this->load_fs_file($class_name);
		} else {
			$fname = $this->base_path . $this->wapi_path . '/' .$class_name.'.php';
			if (file_exists($fname)) {
		    	require_once($fname);
		    	return true;
		    }
			$this->load_app_file($class_name);
		}
	}
    public function __get($name){
        $fn_name = 'get_' . $name;
        if (method_exists($this, $fn_name)){
            return $this->$fn_name();
        } else {
        	$backtrace = debug_backtrace();
			if (in_array($name,$this->session_vars)){
				$svar = $backtrace[0]['class'] . '_' . $name;
				if (isset($_SESSION[$svar])){
					return $_SESSION[$svar];
				}
			} elseif (in_array($name,$this->xsession_vars)){
				$var_table = $backtrace[0]['class'];
				return $this->xget($name,$var_table);
			}
        }
        if (array_key_exists($name,$this->mods)){
        	return $this->mods[$name];
        }
        return null;
    }
    public function __set($name, $value){
        $fn_name = 'set_' . $name;
        if (method_exists($this, $fn_name)){
            $this->$fn_name($value);
            return true;
        } else {
        	$backtrace = debug_backtrace();
			if (in_array($name,$this->session_vars)){
				$svar = $backtrace[0]['class'] . '_' . $name;
				$_SESSION[$svar] = $value;
				return true;
			} elseif (in_array($name,$this->xsession_vars)){
				$var_table = $backtrace[0]['class'];
				$this->xset($name,$value,$var_table);
				return true;
			}
        }
        return false;
    }
	private function auto_db_base_path(){
		// probably temporary function
		// places the database folder
		$docroot = rtrim(getenv('DOCUMENT_ROOT'),'/');
		$default_db_path = $docroot . '/db/';
		$fpath = __FILE__;
		// xampp dev
		$path_check['dev'] = ":\\";
		$path_set['dev'] = 'c:/utils/db/spawnus_db/';
		// vPav
		$path_check['vPav'] = '/hive/';
		$path_set['vPav'] = '/var/www/db/';
		// Spawndev
		$path_check['spawndev'] = 'spawndev.com';
		$path_set['spawndev'] = '/mnt/w0803/d18/s05/b031fcbc/www/spawndev_db/';
		// Spawnus
		$path_check['spawnus'] = 'spawnus.com';
		$path_set['spawnus'] = '/mnt/w0803/d18/s05/b031fcbc/www/spawnus_db/';
		// Check
		foreach ($path_check as $key => $val) {
			if (strstr($fpath,$val)) {
				if (isset($path_set[$key]) && $path_set[$key] != ''){
					return $path_set[$key];
				}
			}
		}
		return $default_db_path;
	}
	private function setup_db_path(){
		$hta = $this->db_base_path . '.htaccess';
		if ($this->create_folder($this->db_base_path)){
			if (!is_file($this->db_base_path . '.htaccess')){
				$rules = "deny from all\n";
				file_put_contents($hta,$rules);
			}
			return true;
		}
		return false;
	}
	private function load_app_file($app_class){
		// TODO Check permission to load this app file
		$fname = $this->base_path . $this->apps_path . '/' . $app_class . '/app.php';
	    if (file_exists($fname)) {
	    	require_once($fname);
	    }
	}
	private function load_fs_file($class){
		// TODO Check permission to load this app file
		$fname = $this->base_path . $this->lib_path . '/file_systems/' . $class . '.php';
		//$fname = $this->base_path . $this->apps_path . '/' . $fs . '/app.php';
	    if (file_exists($fname)) {
	    	require_once($fname);
	    }
	}
	private function load_lib_file($class){
		// TODO Check permission to load this lib file
		$fname = $this->base_path . $this->lib_path . '/' . $class . '.php';
		$folder = substr($class, 0, strlen($class) - 4);
		$fname2 = $this->base_path . $this->lib_path . '/' . $class . '/' . $class . '.php';
		$fname3 = $this->base_path . $this->lib_path . '/' . $folder . '/' . $class . '.php';
	    if (file_exists($fname)) {
	    	require_once($fname);
	    } elseif (file_exists($fname2)) {
	    	require_once($fname2);
	    } elseif (file_exists($fname3)) {
	    	require_once($fname3);
	    }
	}
	private function reset_session(){
		$_SESSION = array();
	}
	public function kill_session(){
		// Unset all of the session variables.
		$_SESSION = array();
		// If it's desired to kill the session, also delete the session cookie.
		// Note: This will destroy the session, and not just the session data!
		if (ini_get("session.use_cookies")) {
		    $params = session_get_cookie_params();
		    setcookie(session_name(), '', time() - 42000,
		        $params["path"], $params["domain"],
		        $params["secure"], $params["httponly"]
		    );
		}
		// Finally, destroy the session.
		session_destroy();
	}
	private function init_paths() {
		$this->base_path = rtrim(getenv('DOCUMENT_ROOT'),'/');
		$this->create_folder($this->db_base_path);
	}
	private function init_app($proc_file){
		$path_parts = pathinfo($proc_file);
		$this->app_file = $path_parts['basename'];
		$this->app_path = $path_parts['dirname'];
		$this->app_rel_path = substr($this->app_path,strlen($this->base_path));
		if (!$this->app_class_init()){
			return false;  // returns false if app not available
		}
		if (!$proc_file || !file_exists($proc_file)){
			return false;
		}
		return true;
	}
	function get_app_db($app_class,$getUserDb=false,$must_exist=false){
		$db_id = 'app';
		$db = null;
		if ($getUserDb)
			$db_id = $this->user->user_id;
		$db_file = $this->db_base_path . $app_class . '/' . $db_id . '.db';
		$this->create_folder($this->db_base_path . $app_class);
		if (file_exists($db_file) || !$must_exist) {
			$db = new $this->db_lib();
			$db->file = $db_file;
		}
		return $db;
	}
	function com_echo($data){
		echo $data;
	}
	function com_ping($data){
//		echo $this->table_str($this->db,'cron_run',0);
//		echo $this->table_str($this->db,'cron',0);
//		echo $this->table_str($this->db,'vartable_cron',0);
//		echo $this->table_str($this->db,'vartable_debug',0);
//		echo $this->table_str($this->db,'proc',0);
//		$sql = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name";
//		echo $this->table_str($this->db,$sql);
		$this->has_focus = $data->has_focus;
		if ($this->has_focus != ''){
			$this->apps->load($this->has_focus);
		}
		return $this->user;
	}
	function com_user($data){
		return $this->user;
	}
	function com_app_list($data){
		$mstr = '[{"name":"login","title":"Login","link":"#login","icon":"/lib/images/keys.png"},{"name":"run","title":"Runner","link":"#main_menu","icon":"/apps/run/app_icon.png"}]';
		$this->user->app_list = json_decode($mstr);
		return $this->user->app_list;
	}
	function com_login($data) {
		# vaildate input first
		if (!isset($data->user) || !isset($data->pass)){
			$this->resp->error = 'Error';
		}
		if (!preg_match("/^[a-zA-Z0-9_]{5,16}$/", $data->pass)) {
		    $this->resp->error = 'Bad password.';
		}
		if (!preg_match("/^[a-zA-Z0-9_]{3,16}$/", $data->user)) {
		    $this->resp->error = 'Bad username.';
		}
		if ($this->resp->error == '') {
			if ($this->users->verify($data->user,$data->pass)){
				// prepare to logout current user
				// ...
				// then login
				$this->users->login($data->user,$data->pass);
			}
		}
		return $this->user;
	}
	function com_load($data){
		// add anti-sql inject!
		if ($this->apps->load($data->app)){
			if (!isset($this->user->running[$data->app])){
				$this->apps->apps[$data->app]->load();
				$this->user->running[$data->app] = '1';
				$_SESSION['WAPI_APPS_RUNNING'] = $this->user->running;
			}
		}
		return $this->apps->resources($data->app);
	}
	function com_get_resources($data){
		// add anti-sql inject!
		return $this->apps->resources($data->app);
	}
	function com_unload($data){
		// add anti-sql inject
		$ret = false;
		if (isset($this->user->running[$data->app])){
			if ($this->apps->load($data->app)){
				$ret = !$this->apps->apps[$data->app]->unload();
			}
			unset($this->user->running[$data->app]);
			$_SESSION['WAPI_APPS_RUNNING'] = $this->user->running;
		}
		$this->apps->unload($data->app);
		return $ret;
	}
	function com_getXPage($data){
		// permission check! admin only
		if (!isset($data->url)){
			echo 'Error';
			return false;
		} else if (strpos($data->url,'http://') !== 0) {
			echo 'Error';
			return false;
		}
		if (!isset($data->php_query)){
			$pq = '';
		} else {
			$pq = $data->php_query;
		}
		$ret = file_get_contents($data->url);
		if ($ret === false) {
			return false;
		} else {
			if ($pq != '') {
				phpQuery::newDocumentHTML($ret, $charset = 'utf-8');
				foreach(pq($pq) as $dom) {
			        $tagName = $dom->tagName;
			        $childNodes = $dom->childNodes;
			        $tmp = pq($dom)->htmlOuter();
			        echo $tmp;
				}
			} else {
				echo $ret;
			}
			return true;
		}
	}
	function text($target,$text,$append=false){
		if (!$append) {
			$this->js("$('$target').html('$text')");
		} else {
			$this->js("$('$target').append('$text')");
		}
	}
	private function db_create(){

	}
	private function db_user_create(){
		// Called on new user first run

	}
	// App enum code
	private function app_table_refresh ($force_update = false) {
	    $directory = $this->base_path . $this->apps_path;
	    $handler = opendir($directory);
	    while ($file = readdir($handler)) {
	        if ($file != '.' && $file != '..') {
	            $fullpath = $directory . '/' . $file;
	            if (is_dir($fullpath)) {
					if (file_exists($fullpath . '/app.xml')){
						$xmlstr = file_get_contents($fullpath . '/app.xml');
						$xml = new SimpleXMLElement($xmlstr);
						$app_name = (string) $xml->name;
						$app_class = (string) $xml->app_class;
						$app_version = (string) $xml->version;
						$app_xml_md5 = md5($xmlstr);
						$xml = null;
		                $sajaFunc = $fullpath . '/app.php';
		                $appfile = $fullpath . '/index.php';
		                // TODO validate classname and appname
		                if ($app_name != '') {
		                	if (file_exists($sajaFunc) && $app_class != '') {
		                		$this->app_add($app_class,$app_name,$app_version,$app_xml_md5,'app',true,$force_update);
		                	} elseif (file_exists($appfile)) {
								$this->app_add($app_class,$app_name,$app_version,$app_xml_md5,'index',true,$force_update);
		                    }
		                }
					}
	            }
	        }
	    }
	    closedir($handler);
	}
	function boolToInt($bool){
		if ($bool)
			return 1;
		else
			return 0;
	}
	function _errorHandler($errno, $errstr, $errfile, $errline){
		// 0 = nothing
		// 1 = E_USER_ERROR
		// 2 = E_USER_ERROR & E_USER_WARNING
		// 3 = E_USER_ERROR & E_USER_WARNING & E_USER_NOTICE
		// 4 = ALL
		$ret = '';
		$silent_mode = false;
		$verbose_level = 4;
		if (!$silent_mode){
		    switch ($errno) {
		    case E_USER_ERROR:
		    	if ($verbose_level >= 1){
			        $ret .= "<b>Wapi ERROR</b> [$errno] $errstr<br />\n";
			        $ret .= "Error on line $errline in file $errfile<br />\n";
			        $ret .= "PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
			        $ret .= "Aborting...<br />\n";
		    	}
		        //exit(1);
		        break;

		    case E_USER_WARNING:
		    	if ($verbose_level >= 2){
		        	$ret .= "<b>Wapi WARNING</b> [$errno] $errstr<br />\n";
		        	$ret .= "Error on line $errline in file $errfile<br />\n";
		    	}
		        break;

		    case E_USER_NOTICE:
		    	if ($verbose_level >= 3){
		        	$ret .= "<b>Wapi NOTICE</b> [$errno] $errstr<br />\n";
		        	$ret .= "Error on line $errline in file $errfile<br />\n";
		    	}
		        break;

		    default:
		    	if ($verbose_level >= 4){
		        	$ret .= "Unknown Wapi error type: [$errno] $errstr<br />\n";
		        	$ret .= "Error on line $errline in file $errfile<br />\n";
		    	}
		        break;
		    }
		}
		if ($ret != ''){
			$this->error($ret);
		}
	    /* Don't execute PHP internal error handler */
	    return true;
	}
	private function msort($array, $id="id", $sort_ascending=true) {
	    $temp_array = array();
	    while(count($array)>0) {
			$lowest_id = 0;
			$index=0;
			foreach ($array as $item) {
			    if (isset($item[$id])) {
				if ($array[$lowest_id][$id]) {
				    if (strtolower($item[$id]) < strtolower($array[$lowest_id][$id])) {
					$lowest_id = $index;
				    }
				}
			    }
			    $index++;
			}
			$temp_array[] = $array[$lowest_id];
			$array = array_merge(array_slice($array, 0,$lowest_id), array_slice($array, $lowest_id+1));
	    }
	    if ($sort_ascending) {
			return $temp_array;
	    } else {
			return array_reverse($temp_array);
	    }
	}
	private function create_folder($dirname) {
		$dirname = rtrim($dirname, '/');
	    $filename = rtrim($dirname,'/') . "/";
	    if (is_dir($filename)) {
	        return true;
	    } else {
	        mkdir($dirname, 0777, true);
		    if (is_dir($filename)) {
		        return true;
		    } else {
		        return false;
		    }
	    }
	}
	private function app_session_reset(){
		# Unset all app session variables except needed ones
	    foreach ($_SESSION['APPS'][$this->class] as $key => $val) {
	    	$unset_it = true;
        	if ($key == 'APP_NAME')
        		$unset_it = false;
			if ($unset_it)
				unset($_SESSION['APPS'][$this->class][$key]);
    	}
	}

	// Wapi cross session variables
	public function xset($var_name,$var_val,$var_table='default') {
		$var_table = 'vartable_' . $var_table;
		// $var_val = base64_encode(serialize($var_val));
		$var_val = $this->db->esc(serialize($var_val));
		$this->db->open();
		$sql = "CREATE TABLE $var_table (var_name TEXT UNIQUE NOT NULL,var_value TEXT NOT NULL)";
		if ($this->db->table_exists($var_table,$sql)){
			$sql = "INSERT OR REPLACE INTO $var_table (var_name, var_value) values ('$var_name','$var_val')";
			$this->db->exec($sql);
		} else {
			$var_val = '';
		}
	    $this->db->close();
	    return strlen($var_val);
	}
	public function xget($var_name,$var_table='default') {
		$ret = null;
		$var_table = 'vartable_' . $var_table;
		$sql = "SELECT var_value FROM $var_table WHERE var_name='$var_name'";
		if ($this->db->query($sql)){
			$res = $this->db->results;
	        foreach ($res as $row){
	        	// $ret = unserialize(base64_decode($row['var_value']));
	        	$ret = unserialize($row['var_value']);
	        	break;
	        }
	    }
	    return $ret;
	}
	public function xunset($var_name,$var_table='default') {
		$ret = null;
		$var_table = 'vartable_' . $var_table;
		$sql = "DELETE FROM $var_table WHERE var_name='$var_name'";
		return $this->db->exec($sql);
	}
	public function user_xunset($var_name,$var_table='default') {
		$ret = null;
		$var_table = 'vartable_' . $var_table;
		$sql = "DELETE FROM $var_table WHERE var_name='$var_name'";
		return $this->db_user->exec($sql);
	}
	// - User cross session variables
	public function user_xset($var_name,$var_val,$var_table='default') {
		$var_table = 'vartable_' . $var_table;
		$var_val = base64_encode(serialize($var_val));
		$this->db_user->open();
		$sql = "SELECT name FROM sqlite_master WHERE type='table' and name='$var_table'";
		if ($this->db_user->query($sql)){
        	$fnd = true;
        } else {
        	$fnd = false;
        }
	    if (!$fnd){
			$sql = "CREATE TABLE $var_table (var_name TEXT UNIQUE NOT NULL,var_value TEXT NOT NULL)";
			$this->db_user->exec($sql);
	    }
		$sql = "INSERT OR REPLACE INTO $var_table (var_name, var_value) values ('$var_name','$var_val')";
		$this->db_user->exec($sql);
	    $this->db_user->close();
	}
	public function user_xget($var_name,$var_table='default') {
		$ret = null;
		$var_table = 'vartable_' . $var_table;
		$sql = "SELECT var_value FROM $var_table WHERE var_name='$var_name'";
		if ($this->db_user->query($sql)){
			$res = $this->db_user->results;
	        foreach ($res as $row){
	        	$ret = unserialize(base64_decode($row['var_value']));
	        	break;
	        }
	    }
	    return $ret;
	}
	private function user_init(){
//		$this->apps_save();
//		$this->apps = null;
//		$this->db_user_init();
	}
	private function db_user_init(){
		$this->db_user = $this->get_app_db($this->class,true);
		if (!$this->db_user->db_exists){
			$this->firstrun_user = true;
			$this->db_user_create();
		}
	}
	public function js($js){
		$app = $this->job->app;
		if ($app == '') $app = 'wapi';
		if ($this->job->type == 'req'){
			$this->resp->req_resp->cmds[] = $js;
		} else if ($this->job->type == 'poll' || $app == 'wapi'){
			if ($this->poll_resp_exists($app,true)) {
				$this->resp->poll_resp[$app]->cmds[] = $js;
			}
		}
	}
	public function error($err){
		$app = $this->job->app;
		if ($app == '') $app = 'wapi';
		if ($this->job->type == 'req'){
			$this->resp->req_resp->errors[] = $err;
		} else if ($this->job->type == 'poll' || $app == 'wapi'){
			if ($this->poll_resp_exists($app,true)) {
				$this->resp->poll_resp[$app]->errors[] = $err;
			}
		}
	}
	public function error_cnt(){
		$ret = 0;
		$app = $this->job->app;
		if ($app == '') $app = 'wapi';
		if ($this->job->type == 'req'){
			$ret = count($this->resp->req_resp->errors);
		} else if ($this->job->type == 'poll' || $app == 'wapi'){
			if ($this->poll_resp_exists($app)) {
				$ret = count($this->resp->poll_resp[$app]->errors);
			}
		}
		return $ret;
	}
	public function poll_resp_exists($app,$create = false){
		if (!isset($this->resp->poll_resp[$app])){
			if ($create){
				$this->resp->poll_resp[$app] = new wapi_response();
				$this->resp->poll_resp[$app]->app = $app;
				return true;
			} else {
				return false;
			}
		} else {
			return true;
		}
	}
	public function wappLog($txt){
		$this->js("wapp.log('" . $txt . "')");
	}
	public function play($audio_file){
		$cmd = '';
		if ($this->proc->cmd_exists('play')) {
			$cmd = 'play ' . $audio_file;
		} else if ($this->proc->cmd_exists('aplay')) {
			$cmd = 'aplay ' . $audio_file;
		}
		if ($cmd != '') {
			$cmd_full = $cmd . ' &> /dev/null < /dev/null & echo $!';
			$pid = shell_exec($cmd_full);
			return $pid;
		} else {
			return -1;
		}
	}
	public function table_str($db,$table,$start_i=-1,$cnt=50){
		if (preg_match('#\s#',$table)){
			$sql = $table;
		} else {
			$sql = "SELECT * FROM $table";
			if ($start_i > -1){
				$sql = "SELECT * FROM $table LIMIT $start_i,$cnt";
			}
		}
		$ret = '';
		if ($db->query($sql)){
			$res = $db->results;
			echo count($res) . " - " . $sql . "<br />\n";
			$table_css = "color:black; border-width: 1px;border-spacing: 0;border-style: outset; border-color: black; border-collapse: separate; background-color: white;";
			$th_css = "border-width: 1px;padding: 1px;border-style: inset;border-color: gray;background-color: white;";
			$td_css = "border-width: 1px;padding: 1px;border-style: inset;border-color: gray;background-color: white;";
			$ret = '<table border="1" style="'.$table_css.'">';
			// Column Headers
			$ret .= "<tr>";
			foreach ($res[0] as $k => $v){
			    $ret .= "<th style=\"$th_css\">" . $k . "</th>";
			}
			$ret .= "</tr>";
//			// Data
			foreach ($res as $r){
				$ret .= "<tr>";
				foreach ($r as $v){
					if (strlen($v) >= 100) {
						$v = substr($v,0,100) . " ...";
					}
				    $ret .= "<td style=\"$td_css\">" . $v . "</td>";
				}
				$ret .= "</tr>";
			}
			$ret .= "</table>";
		}
		return $ret;
	}
	private function load_mod($mod,$force_reload=false){
		if (!isset($this->mods[$mod]) || $force_reload){
			$this->mods[$mod] = new $mod();
		}
	}
	private function key_gen($key_len){
		$ret = '';
		while (strlen($ret) <= $key_len){
			$ret .= sha1(uniqid(rand(), true));
		}
	    return substr($ret, 0, $key_len);
	}
	public function event_user_loaded(){
		$this->db_user_init();
	}
	function run(){
		if ($this->ran) {
			return false;
		}
		// wapi init
		$this->ran = true;
		ob_start();
		$this->base_path = rtrim(getenv('DOCUMENT_ROOT'),'/');
		$this->db_base_path = $this->auto_db_base_path();
		$this->setup_db_path();
		$this->db = $this->get_app_db($this->class);
		if (!$this->db->db_exists) {
			echo "Wapi first run<br />\n";
			$this->device_id = $this->key_gen(128);
			$this->firstrun = true;
			$_SESSION = array();
		}
		if ($this->session_id != session_id()){
			$this->session_id = session_id();
			$this->firstrun_session = true;
		}
		$this->mods['users'] = new wapi_users();
		$this->mods['events'] = new wapi_events();
		$this->mods['groups'] = new wapi_groups();
		$this->mods['proc'] = new wapi_proc();
		$this->mods['cron'] = new wapi_cron();
		$this->mods['resp'] = new wapi_response();
		$this->mods['apps'] = new wapi_apps();
		$this->events->raise('mods_loaded');
		if ($this->user->user_id == ''){
			$this->users->login('Guest','guest');
		} else {
			$this->users->load_user($this->user->user_id);
		}
		$this->proc->poll();
		if ($this->cron->poll_check()){
			$this->cron->poll();
			$ret = ob_get_contents();
			ob_end_clean();
			echo $ret;
			return true;
		} else {
			if (isset($_GET['cron_id'])) {
				$ret = ob_get_contents();
				ob_end_clean();
				echo $ret;
				return false;
			}
		}
		// Process request data
		$data = '';
		// Get the request in any accepted form
		if (isset($_POST['data']) && $_POST['data'] != '') {
			$this->job->type = 'req';
			$data = $_POST['data'];
			$data = stripslashes($data);
			try {
				$this->req = json_decode($data);
			} catch (Exception $e) {
				$msg = $e->getMessage();
			}
		}
		if ($this->req) {
			if ($this->req->min){
				$this->req->min = true;
			} else {
				$this->req->min = false;
			}
			if (!preg_match("/^[a-zA-Z0-9_]{2,32}$/", $this->req->app)) {
				if ($this->req->app != '') {
			    	$this->error('WAPI: Invalid app');
			    	$this->req->app = '';
			    	$this->req->com = '';
				}
			}
			if (!preg_match("/^[a-zA-Z0-9_]{2,32}$/", $com = $this->req->com)) {
				if ($this->req->com != '') {
			    	$this->error('WAPI: Invalid function');
			    	$this->req->com = '';
				}
			}
			if ($this->req->app == '' && $this->req->com != '')
				$this->req->app = 'wapi';
			// End Process request data
			// load req app and run com
			$app = $this->req->app;
			$com = $this->req->com;
			$this->job->app = $app;
			if (count($this->resp->req_resp->errors) == 0){
				$this->app_has_focus = $app;
				// TODO parse commands and check permissions
				if ($app != 'wapi') {
					if (!$this->apps->load($app)) {
						$this->wappLog('WAPI: Failed to load ' . $app);
						$this->resp->req_resp->errors = 'WAPI: Failed to load app';
					} else {
						$this->wappLog('WAPI: Loading ' . $app);
					}
				}
				if (count($this->resp->req_resp->errors) == 0){
					if ($com != ''){
						// Running request NOW
						ob_start();
						$this->resp->req_resp->data = $this->apps->com($app,$com,$this->req->data);
						$this->resp->req_resp->text = ob_get_clean();
						$this->job->type = '';
						$this->job->app = '';
						// Done running request
					}
				}
			}
		}
		// Load any other running apps if not in min_mode
		if (!isset($this->req->min) || !$this->req->min) {
			$this->wappLog('WAPI: Loading apps');
			$this->apps->load_all();
		} else {
			$this->wappLog('WAPI: min_mode');
		}
		// Poll running apps
		$this->wappLog('WAPI: Polling');
		$this->apps->poll();
        ob_end_clean();
		//$this->wappLog('WAPI: Done');
		$ret = json_encode($this->resp);
		// Send data
		if (preg_match('#^'.$this->apps_path.'/(?<app>[a-zA-Z0-9_]{2,32})/|$#i',$_SERVER['SCRIPT_NAME'],$matches)){
			// app index file
			if (isset($matches['app'])){
				$appdir = $matches['app'];
			}
		}
		if ($this->req) {
			// If a valid request was recvd
			echo utf8_encode($ret);
		} elseif (preg_match('#^'.$this->apps_path.'/(?<app>[a-zA-Z0-9_]{2,32})/index.php$#i',$_SERVER['SCRIPT_NAME'],$matches)){
			// app index file
			//echo $matches['app'];
			$stop = '';
		} else {
//			///////////////////////////////////////////////////////////////////////
//			echo 'Wapi by SpawnDev' . "<br />";
//			echo $this->resp->text . "<br />";
//			if (isset($_REQUEST['c'])) {
//				if ($_REQUEST['c'] == '1') {
//					$this->cron->add('Access Denied','play /var/root/sounds/star_trek/computer_access_denied.wav','* * * * *');
//					//$this->cron->add('Access Denied','play /var/root/sounds/star_trek/computer_access_denied.wav','0,5,10,15,20,25,30,35,40,45,50,55 * * * *');
//					echo "New job added<br />\n";
//				} elseif ($_REQUEST['c'] == '2') {
//					$this->cron->rem('Access Denied');
//					echo "Job removed<br />\n";
//				} elseif ($_REQUEST['c'] == '3') {
//					$this->cron->job_enabled('Access Denied',false);
//					echo "Job disabled<br />\n";
//				} elseif ($_REQUEST['c'] == '4') {
//					$this->cron->job_enabled('Access Denied',true);
//					echo "Job enabled<br />\n";
//				} elseif ($_REQUEST['c'] == 'a') {
//					$this->cron->add('big','wget --tries 10 -O /dev/null http://192.168.0.130/TVersity_1_9_2_Pro_1.rar','* * * * *');
//					echo "New job added<br />\n";
//				} elseif ($_REQUEST['c'] == 'u') {
//					$this->cron->cron_pause();
//					echo 'Cron Paused' . "<br />\n";
//				} elseif ($_REQUEST['c'] == 'l') {
//					$this->cron->cron_play();
//					echo 'Cron Play' . "<br />\n";
//				} elseif ($_REQUEST['c'] == 'r') {
//					$this->cron->rem_all();
//					echo 'Cron all removed' . "<br />\n";
//				} elseif ($_REQUEST['c'] == 'z') {
//					$this->play('/var/root/sounds/star_trek/computer_access_denied.wav');
//					echo 'PLAY' . "<br />\n";
//				} elseif ($_REQUEST['c'] == 'o') {
//					$this->cron->rem_all();
//					$dbp = rtrim($this->db_base_path,'/');
//					$this->proc->run('sleep 16; rm ' . $dbp . '/wapi/*.db; play /var/root/sounds/star_trek/computer_access_granted.wav');
//					echo 'Wapi DB Wiped' . "<br />\n";
//				} elseif ($_REQUEST['c'] == 'm') {
//					$this->cron->add('Date','sleep 12; date','* * * * *');
//					echo 'DateJob' . "<br />\n";
//				}elseif ($_REQUEST['c'] == 'list') {
//					echo '<table border="1">';
//					foreach ($_SERVER as $k => $v){
//					    echo "<tr><td>" . $k ."</td><td>" . $v . "</td></tr>";
//					}
//					echo "</table>";
//				}
//
//
//			}
//			echo "User ID: " . $this->user->user_id . "<br />\n";
//			echo "User Name: " . $this->user->first_name . " " . $this->user->last_name . "<br />\n";
//			echo "DB_Path: $this->db_base_path <br />\n";
//			if ($this->err_str != ''){
//				echo "Errors: <br/>\n" . $this->err_str;
//			}
//			/////////////////////////////////////////////////////////////////////////
		}
	}
}
$wapi = new wapi();
function errorHandler($errno, $errstr, $errfile, $errline){
	global $wapi;
	$debug = true;
	if ($debug) {
		$msg = "Err Loc: " . $errline . " " . $errfile . "<br/>";
		$msg += "Err: " . $errfile . "<br/>";
		$wapi->error($msg);
	}
	return $wapi->_errorHandler($errno, $errstr, $errfile, $errline);
}
function __autoload($class_name) {
	global $wapi;
	$wapi->_autoload($class_name);
}
$old_handler = set_error_handler("errorHandler");
$new_session = false;
//$new_session = true;
if ($new_session){
	$wapi->kill_session();
	echo 'Session killed';
} else {
	$wapi->run();
}
?>